﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Emgu.CV;
using Emgu.CV.ML;
using Emgu.CV.Structure;
using MatrixLibrary;

namespace finProj.Entities
{
    internal class KMeans
    {
        private Matrix<float> Vectors_storage;
        private int VectorsSize;
        private int NumberOfVectorsInStorage;
        private int Num_of_People ;
        private Matrix<Int32> labels;
        private int attempts=20;
        private Matrix<float> centers;
        
 
        //////////////////////////////////////////////functions:geters///////////////////////////////////////////////////
        /// 
        public IntPtr GetLables()
        {
            return (this.labels);
        }

        //public Matrix<Single> GetCenters()
        //{
        //    return (this.centers);
        //}

        public Matrix<Single> GetMatrix()
        {
            return (Vectors_storage);
        }


        public int DoKmeansAlgorithm()
        {

            this.VectorsSize = Settings.Instance.existingKeys.Count;
            this.NumberOfVectorsInStorage = Settings.Instance.picManager.Pictures.Count;
            this.Num_of_People = Settings.Instance.numberOfPeople;
            //this.centers = new Matrix<float>(1,Num_of_People);
            float[,] samples = new float[NumberOfVectorsInStorage, VectorsSize];

            if (NumberOfVectorsInStorage == 0)
            {
                System.Console.WriteLine("your vectors storage is empty");
                return (0);
            }
            if (VectorsSize == 0)
            {
                System.Console.WriteLine("your vectors is empty");
                return (0);
            }

            var featureKeys = Settings.Instance.existingKeys;

            for (int i = 0; i < NumberOfVectorsInStorage; i++)
            {
                for (int j = 0; j < VectorsSize; j++)
                {
                    if (Settings.Instance.picManager.Pictures[i].FeaturesVec.features.ContainsKey(featureKeys[j]))
                    {
                        samples[i, j] = (float)Settings.Instance.picManager.Pictures[i].FeaturesVec.features[featureKeys[j]];
                           
                    }
                    else
                    {
                        samples[i, j] = 0;
                    }
                }
            }

            float[,] cent1 = new float[Num_of_People, Settings.Instance.existingKeys.Count];
            this.centers = new Matrix<float>(cent1);
            
            MCvTermCriteria term = new MCvTermCriteria();
            this.Vectors_storage = new Matrix<float>(samples);

            this.labels = new Matrix<Int32>(NumberOfVectorsInStorage, 1);

           
            /*-------------------------------------------------------------------------------------------------------------*/
         
            CvInvoke.cvKMeans2(Vectors_storage, Num_of_People, labels, term, attempts, IntPtr.Zero, 0, centers, IntPtr.Zero);

            var clsmngr = new ClusterManager();

            for (int clusternum = 0; clusternum < Num_of_People; clusternum++)
            {
                var cluster = new Cluster();
                var featVec = new float[VectorsSize];
                //   build centroid from centers
                for (int i = 0; i < VectorsSize; i++)
                {
                    featVec[i] = centers[clusternum,i];
                }

                cluster.centroid = featVec;

                //   build cluster picture list from labels
                for (int j = 0; j < NumberOfVectorsInStorage; j++)
                {
                    if (labels[j,0] == clusternum)
                    {
                        cluster.pictures.Add(Settings.Instance.picManager.Pictures[j]); //add picture to cluster
                    }
                }

                clsmngr.clusters.Add(cluster);
            }

            Settings.Instance.clusterManager = clsmngr;


                return (1);
            #region commented
            // labels = new IntPtr(NumberOfVectorsInStorage);
    //centers = new Matrix<Double>(Num_of_People, VectorsSize);
    // centers = new Matrix<float>(Num_of_People, VectorsSize);
    // Matrix<float> vects = Vectors_storage.Convert<float>();

    //  IntPtr vecs = Vectors_storage;
    //  IntPtr cntrs = centers;

    //cvKMeans2(IntPtr samples, int clusterCount, IntPtr labels, MCvTermCriteria termcrit, int attempts, IntPtr rng, int flags, IntPtr centers, IntPtr compactness);
    //  int kFlag = CvInvoke.cvKMeans2(vects, Num_of_People, labels, new MCvTermCriteria(1000), attempts, IntPtr.Zero, 0, centers, IntPtr.Zero);
    //  if(kFlag==0)
    ////  {
    //    return (0);
    //  }
            //  return (1);
            #endregion commented
        }

        public Cluster normalizeClusterKmeans(Cluster cls)
        {
            this.VectorsSize = Settings.Instance.existingKeys.Count;
            this.NumberOfVectorsInStorage = cls.pictures.Count;
            this.Num_of_People = 1;
            float[,] samples = new float[NumberOfVectorsInStorage,VectorsSize];


            if (NumberOfVectorsInStorage == 0)
            {
                System.Console.WriteLine("your vectors storage is empty");
                cls.centroid = null;
                cls.centroid = new float[VectorsSize];
                return cls;
            }
            if (VectorsSize == 0)
            {
                System.Console.WriteLine("your vectors are empty");
                return cls;
            }

            var featureKeys = Settings.Instance.existingKeys;

            for (int i = 0; i < NumberOfVectorsInStorage; i++)
            {
                for (int j = 0; j < VectorsSize; j++)
                {
                    if (cls.pictures[i].FeaturesVec.features.ContainsKey(featureKeys[j]))
                    {
                        samples[i, j] =
                            (float) cls.pictures[i].FeaturesVec.features[featureKeys[j]];

                    }
                    else
                    {
                        samples[i, j] = 0;
                    }
                }
            }

            float[,] cent1 = new float[Num_of_People, VectorsSize];
            this.centers = new Matrix<float>(cent1);

            MCvTermCriteria term = new MCvTermCriteria();
            this.Vectors_storage = new Matrix<float>(samples);

            this.labels = new Matrix<Int32>(NumberOfVectorsInStorage, 1);


            /*-------------------------------------------------------------------------------------------------------------*/

            CvInvoke.cvKMeans2(Vectors_storage, Num_of_People, labels, term, attempts, IntPtr.Zero, 0, centers,
                               IntPtr.Zero);

            for (int clusternum = 0; clusternum < Num_of_People; clusternum++)
            {
                var cluster = new Cluster();
                var featVec = new float[VectorsSize];
                //   build centroid from centers
                for (int i = 0; i < VectorsSize; i++)
                {
                    featVec[i] = centers[clusternum, i];
                }

                cluster.centroid = featVec;

                //   build cluster picture list from labels
                for (int j = 0; j < NumberOfVectorsInStorage; j++)
                {
                    if (labels[j, 0] == clusternum)
                    {
                        cluster.pictures.Add(cls.pictures[j]); //add picture to cluster
                    }
                }
                cls = cluster;
                return cls;
            }
            return cls;
        }

        public int findCluster(Picture pic)
        {
            List<Cluster> clusters = Settings.Instance.clusterManager.clusters;

            this.VectorsSize = Settings.Instance.existingKeys.Count;
            this.NumberOfVectorsInStorage = clusters.Count + 1;
            this.Num_of_People = Settings.Instance.numberOfPeople;

            float[,] samples = new float[NumberOfVectorsInStorage, VectorsSize];

            if (NumberOfVectorsInStorage == 0)
            {
                System.Console.WriteLine("your vectors storage is empty");
                return (0);
            }
            if (VectorsSize == 0)
            {
                System.Console.WriteLine("your vectors is empty");
                return (0);
            }
            
            var featureKeys = Settings.Instance.existingKeys;

            for (int j = 0; j < VectorsSize; j++)
                {
                    if (pic.FeaturesVec.features.ContainsKey(featureKeys[j]))
                    {
                        samples[0,j] = (float)pic.FeaturesVec.features[featureKeys[j]];      
                    }
                }


            for (int i = 1; i < NumberOfVectorsInStorage; i++)
            {
                for (int j = 0; j < VectorsSize; j++)
                {
                        samples[i, j] = clusters[i-1].centroid[j];
                }
            }

            float[,] cent1 = new float[Num_of_People, Settings.Instance.existingKeys.Count];
            this.centers = new Matrix<float>(cent1);

            MCvTermCriteria term = new MCvTermCriteria();
            this.Vectors_storage = new Matrix<float>(samples);

            this.labels = new Matrix<Int32>(NumberOfVectorsInStorage, 1);


            /*-------------------------------------------------------------------------------------------------------------*/

            CvInvoke.cvKMeans2(Vectors_storage, Num_of_People, labels, term, attempts, IntPtr.Zero, 0, centers, IntPtr.Zero);
            int id = labels[0, 0];

            for (int i = 1; i < NumberOfVectorsInStorage; i++)
            {
                if (id == labels[i,0])
                {
                    return i-1;
                }

            }
            return -1;
        }


public int findClusterNEW(Picture pic)
{
    List<Cluster> clusters = Settings.Instance.clusterManager.clusters;

    this.VectorsSize = Settings.Instance.existingKeys.Count;
    this.NumberOfVectorsInStorage = clusters.Count + 1;
    this.Num_of_People = Settings.Instance.numberOfPeople;
    if (NumberOfVectorsInStorage == 0)
    {
        System.Console.WriteLine("your vectors storage is empty");
        return (0);
    }
    if (VectorsSize == 0)
    {
        System.Console.WriteLine("your vectors is empty");
        return (0);
    }
    double sum;
    double[] dis=new double[Settings.Instance.clusterManager.clusters.Count()];
    for(int i=0;i<Settings.Instance.clusterManager.clusters.Count();i++)
    {
        sum = 0;
        for(int j=0;j<Settings.Instance.existingKeys.Count();j++)
        {
            sum =
                Math.Pow(
                    Math.Abs((clusters[i].centroid[j]) - (pic.FeaturesVec.features[Settings.Instance.existingKeys[j]])),
                    2) + sum;
        }
        dis[i] = Math.Sqrt(sum);
    }
    double min = dis[0];
    int plase = 0;
    for(int i=1;i<Settings.Instance.clusterManager.clusters.Count();i++)
    {
        if(dis[i]<min)
        {
            min = dis[i];
            plase = i;
        }
    }
    return (plase);
}


        #region commented
        /* public KMeans()
        {
            this.VectorsSize = Settings.Instance.existingKeys.Count;
            this.NumberOfVectorsInStorage = 0;
            this.Num_of_People = 0;
            this.attempts = 5;
        }

        public void runKmeansAllimgs()
        {
            var mngr = Settings.Instance.picManager;
            var featureVectors = new FeatureVector[mngr.Pictures.Count];
            for (int i = 0; i < mngr.Pictures.Count; i++)
            {
                featureVectors[i] = mngr.Pictures[i].FeaturesVec;
            }
            NumberOfVectorsInStorage = featureVectors.Length;

            Vectors_storage = new Matrix<Double>(NumberOfVectorsInStorage, featureVectors[0].GetSize());
            int r = 0, c = 0;
            foreach (var featureVector in featureVectors)
            {
                c = 0;


                foreach (var existingKey in Settings.Instance.existingKeys)
                {
                    if (featureVector.features.ContainsKey(existingKey))
                    {
                        Vectors_storage[r, c] = featureVector.features[existingKey];
                        c++;
                    }
                    else
                    {
                        Vectors_storage[r, c] = 0;
                        c++;
                        
                    }

                }
                r++;
            }

            int numOfPpl = Settings.Instance.numberOfPeople;

            int success = DoKmeansAlgorithm(numOfPpl);
        }*/
        #endregion commented
    }

}